﻿using System;
using UtilZ.Dotnet.Ex.Base;

namespace UtilZ.Dotnet.Ex.DataStruct
{
    /// <summary>
    /// 定时任务基类
    /// </summary>
    public abstract class TimingTaskAbs : ITimingTask
    {
        private readonly TimingTaskType _taskType;
        private readonly long _intervalMilliseconds;
        private readonly int _hour;
        private readonly int _minute;
        private readonly int _second;
        private readonly int _millisecond;

        private long _lastExcuteTimestamp = 0L;
        private DateTime _nextExcuteTime;


        /// <summary>
        /// 当前任务已执行次数
        /// </summary>
        private int _excutedCount = 0;
        private readonly int _count;
        /// <summary>
        /// 执行次数,小于0表示无限,大于等于0表示执行指定次数
        /// </summary>
        public int Count
        {
            get { return _count; }
        }

        /// <summary>
        /// 获取或设置与缓存数据关联的用户定义的数据
        /// </summary>
        public object Tag { get; set; }



        /// <summary>
        /// 构造函数-间隔执行
        /// </summary>
        /// <param name="interval">执行间隔</param>
        /// <param name="count">执行次数,小于0表示无限,大于等于0表示执行指定次数</param>
        public TimingTaskAbs(TimeSpan interval, int count)
        {
            this._intervalMilliseconds = (long)interval.TotalMilliseconds;
            this._taskType = TimingTaskType.Interval;
            this._count = count;
        }

        /// <summary>
        /// 构造函数-定时执行
        /// </summary>
        /// <param name="hour">时</param>
        /// <param name="minute">分</param>
        /// <param name="second">秒</param>
        /// <param name="millisecond">毫秒</param>
        /// <param name="count">执行次数,小于0表示无限,大于等于0表示执行指定次数</param>
        public TimingTaskAbs(int hour, int minute, int second, int millisecond, int count)
        {
            this._hour = hour;
            this._minute = minute;
            this._second = second;
            this._millisecond = millisecond;
            this._taskType = TimingTaskType.Timing;
            this._count = count;

            var time = DateTime.Now;
            TimeSpan tsCurrent = new TimeSpan(time.Hour, time.Minute, time.Second, time.Millisecond);
            TimeSpan tsDst = new TimeSpan(hour, minute, second, millisecond);

            if (tsCurrent >= tsDst)
            {
                time = time.AddDays(1);
            }

            this._nextExcuteTime = new DateTime(time.Year, time.Month, time.Day, hour, minute, second, millisecond);
        }

        /// <summary>
        /// 任务名称
        /// </summary>
        public string Name { get; set; } = "匿名定时任务";

        /// <summary>
        /// 获取下次执行需要等待的时长,单位:毫秒
        /// </summary>
        /// <param name="timestamp">当前时间戳</param>
        /// <returns>当前任务到下一次执行的还需要的延迟时间</returns>
        public long GetNextExcuteWaitMilliseconds()
        {
            if (this._taskType == TimingTaskType.Interval)
            {
                if (this._lastExcuteTimestamp > 0)
                {
                    return this._intervalMilliseconds - (TimeEx.GetTimestamp() - this._lastExcuteTimestamp);
                }
                else
                {
                    return 0;
                }
            }
            else
            {
                return (long)(this._nextExcuteTime - DateTime.Now).TotalMilliseconds;
            }
        }

        /// <summary>
        /// 判断当前任务是否需要执行[true:需要执行;false:不需要执行]
        /// </summary>
        /// <returns>true:需要执行;false:不需要执行</returns>
        public bool CanExcute()
        {
            if (this._taskType == TimingTaskType.Interval)
            {
                if (this._lastExcuteTimestamp > 0)
                {
                    return this._intervalMilliseconds - (TimeEx.GetTimestamp() - this._lastExcuteTimestamp) < 0;
                }
                else
                {
                    return true;
                }
            }
            else
            {
                return (long)(this._nextExcuteTime - DateTime.Now).TotalMilliseconds <= 0;
            }
        }

        /// <summary>
        /// 执行任务
        /// </summary>
        public bool Excute()
        {
            this.PrimitiveExcute();
            if (this._count >= 0)
            {
                this._excutedCount++;
            }

            if (this._taskType == TimingTaskType.Interval)
            {
                this._lastExcuteTimestamp = TimeEx.GetTimestamp();
            }
            else
            {
                var time = DateTime.Now.AddDays(1);
                this._nextExcuteTime = new DateTime(time.Year, time.Month, time.Day, this._hour, this._minute, this._second, this._millisecond);
            }

            if (this._count < 0)
            {
                return false;
            }
            else
            {
                return this._excutedCount >= this._count;
            }
        }

        /// <summary>
        /// 执行操作
        /// </summary>
        protected abstract void PrimitiveExcute();

    }

    internal enum TimingTaskType
    {
        /// <summary>
        /// 定时执行
        /// </summary>
        Timing,

        /// <summary>
        /// 间隔执行
        /// </summary>
        Interval
    }
}
